
#ifndef thd4_h__
#define thd4_h__

#pragma once

#include <mutex>
#include <condition_variable>
#include <thread>
#include <queue>
#include <iostream>

namespace thd41
{
	bool more_data_to_prepare()
	{
		static int count = 100;
		--count;
		if (count > 0)
		{
			std::this_thread::sleep_for(std::chrono::milliseconds(100));
			return true;
		}
		return false;
	}

	struct data_chunk
	{
		data_chunk(int i) : value(i) {}

		int value{ 9 };
	};

	data_chunk prepare_data()
	{
		static int tick = 0;
		return data_chunk(++tick);
	}

	void process(data_chunk& data)
	{
		std::cout << data.value << std::endl;
	}

	bool is_last_chunk(data_chunk& data)
	{
		if (data.value > 90)
			return true;
		else
			return false;
	}

	std::mutex mut;
	std::queue<data_chunk> data_queue;
	std::condition_variable data_cond;

	void data_preparation_thread()
	{
		while (more_data_to_prepare())
		{
			data_chunk const data = prepare_data();
			{
				std::lock_guard<std::mutex> lk(mut);
				data_queue.push(data);
			}
			data_cond.notify_one();
		}
		while (!data_queue.empty())
		{
			auto obj = data_queue.front(); data_queue.pop();
			std::cout << obj.value << std::endl;
		}
		std::cout << "data_preparation_thread" << std::endl;
	}

	void data_processing_thread()
	{
		while (true)
		{
			std::unique_lock<std::mutex> lk(mut);
			data_cond.wait(lk, [] {return !data_queue.empty(); });
			data_chunk data = data_queue.front();
			data_queue.pop();
			lk.unlock();
			process(data);
			if (is_last_chunk(data))
				break;
		}
		std::cout << "data_processing_thread" << std::endl;
	}

	int thd41_main()
	{
		std::thread t1(data_preparation_thread);
		std::thread t2(data_processing_thread);

		t1.join();
		t2.join();
		return 1;
	}
}

#endif // thd4_h__